#include "globals.h"
#include "errno.h"
#include "types.h"

#include "mm/mm.h"
#include "mm/tlb.h"
#include "mm/mman.h"
#include "mm/page.h"

#include "proc/proc.h"

#include "util/string.h"
#include "util/debug.h"

#include "fs/vnode.h"
#include "fs/vfs.h"
#include "fs/file.h"
#include "fs/stat.h"
#include "vm/vmmap.h"
#include "vm/mmap.h"

/*
 * This function implements the mmap(2) syscall, but only
 * supports the MAP_SHARED, MAP_PRIVATE, MAP_FIXED, and
 * MAP_ANON flags.
 *
 * Add a mapping to the current process's address space.
 * You need to do some error checking; see the ERRORS section
 * of the manpage for the problems you should anticipate.
 * After error checking most of the work of this function is
 * done by vmmap_map(), but remember to clear the TLB.
 */
/*
 * Don't know how to handle MAP_FIXED and MAP_ANON
 */
int
do_mmap(void *addr, size_t len, int prot, int flags,
        int fd, off_t off, void **ret)
{	
	file_t *f;
	f = fget(fd);

	if(f==NULL && (flags & MAP_ANON)==0) 
		return -EBADF;
	else if(f!=NULL)
	{
		fput(f);
		if(    (S_ISREG(f->f_vnode->vn_mode)!=1) 
		    || (((flags & MAP_PRIVATE) && (f->f_mode & FMODE_READ)!=1)) 
		    || (((flags & MAP_SHARED) && (prot & PROT_WRITE) && ((f->f_mode & FMODE_READ)!=1 && (f->f_mode & FMODE_WRITE)!=1)))
		    || ((prot & PROT_WRITE) && (f->f_mode & FMODE_APPEND)==1 && ((f->f_mode & FMODE_READ)!=1 && (f->f_mode & FMODE_WRITE)!=1))
       		  )
			return -EACCES;
		else if( len==0
			|| (uint32_t)addr>USER_MEM_HIGH 
			|| (uint32_t)addr<USER_MEM_LOW 
			|| ((uint32_t)addr+len)>USER_MEM_HIGH
			|| ((uint32_t)addr+off)>USER_MEM_HIGH  
			|| PAGE_ALIGNED(ADDR_TO_PN(addr))
			|| PAGE_ALIGNED(ADDR_TO_PN(addr+len))
			|| PAGE_ALIGNED(ADDR_TO_PN(addr+off))
			|| (flags & MAP_PRIVATE & MAP_SHARED)
			|| ((flags & MAP_PRIVATE)==0 && (flags & MAP_SHARED)==0 )
		       )
			return -EINVAL;
		else if(fd>=NFILES)
			return -ENFILE;
		tlb_flush_all();
	KASSERT(NULL != curproc->p_pagedir);
	dbg(DBG_ELF,"do_mmap: current process's pagedir is not null\n");
		return vmmap_map(curproc->p_vmmap, f->f_vnode, ADDR_TO_PN(addr), ADDR_TO_PN(addr+len), prot, flags, off, VMMAP_DIR_LOHI,(vmarea_t **)ret);
	}
	else
	{
		return 0;/* NOTE Don't know in this case what to pass to vmmap_map function */
	}
        /*NOT_YET_IMPLEMENTED("VM: do_mmap");
        return -1;*/
}


/*
 * This function implements the munmap(2) syscall.
 *
 * As with do_mmap() it should perform the required error checking,
 * before calling upon vmmap_remove() to do most of the work.
 * Remember to clear the TLB.
 */
int
do_munmap(void *addr, size_t len)
{
	if( len==0
		|| (uint32_t)addr>USER_MEM_HIGH 
		|| (uint32_t)addr<USER_MEM_LOW 
		|| ((uint32_t)addr+len)>USER_MEM_HIGH
		|| PAGE_ALIGNED(ADDR_TO_PN(addr))
		|| PAGE_ALIGNED(ADDR_TO_PN(addr+len))
	       )
		return -EINVAL;
	tlb_flush_all();
 KASSERT(NULL != curproc->p_pagedir);
dbg(DBG_ELF,"do_munmap: current process's pagedir is not null\n");
	return vmmap_remove(curproc->p_vmmap, ADDR_TO_PN(addr), ADDR_TO_PN(addr+len));
/*        NOT_YET_IMPLEMENTED("VM: do_munmap");
        return -1;*/
}

